package net.sansi.v3correctserver.util;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.experimental.Accessors;

/**
 * Created by Fang
 * create time : 2024/10/31
 * IDE : IntelliJ IDEA
 * <p>
 * 三点仿射转换
 */

public class Transformer {

    /**
     * 坐标位置
     */
    @Data
    @AllArgsConstructor
    @Accessors(chain = true)
    public static class Point {
        private double x;
        private double y;
    }

    // 转换矩阵
    private double[] affineTransformation;
    // 原始矩阵
    private final Point[] srcPoints;
    // 目标矩阵
    private final Point[] dstPoints;
    // 反向变换器
    private Transformer reverseTransformer;

    /**
     * 构造函数
     *
     * @param srcPoints 原始矩阵
     * @param dstPoints 目标矩阵
     */
    public Transformer(Point[] srcPoints, Point[] dstPoints) {
        if (srcPoints.length < 3 || dstPoints.length < 3) {
            throw new IllegalArgumentException("srcPoints or dstPoints must have at least 3 points");
        }
        this.srcPoints = srcPoints;
        this.dstPoints = dstPoints;
    }

    /**
     * 应用逆变换
     *
     * @param x
     * @param y
     * @return
     */
    public Point applyReverseTransformation(double x, double y) {
        if (reverseTransformer == null) {
            reverseTransformer = new Transformer(dstPoints, srcPoints);
        }
        return reverseTransformer.applyTransformation(x, y);
    }


    /**
     * 应用仿射变换
     *
     * @param x
     * @param y
     * @return
     */
    public Point applyTransformation(double x, double y) {
        calculateAffineTransformation();
        double[] at = affineTransformation;
        double rx = at[0] * x + at[1] * y + at[2];
        double ry = at[3] * x + at[4] * y + at[5];
        return new Point(rx, ry);
    }

    private void calculateAffineTransformation() {
        if (affineTransformation != null) return;
        double[][] A = new double[][]{
                {srcPoints[0].x, srcPoints[0].y, 1, 0, 0, 0},
                {0, 0, 0, srcPoints[0].x, srcPoints[0].y, 1},
                {srcPoints[1].x, srcPoints[1].y, 1, 0, 0, 0},
                {0, 0, 0, srcPoints[1].x, srcPoints[1].y, 1},
                {srcPoints[2].x, srcPoints[2].y, 1, 0, 0, 0},
                {0, 0, 0, srcPoints[2].x, srcPoints[2].y, 1}
        };
        double[] B = new double[]{
                dstPoints[0].x,
                dstPoints[0].y,
                dstPoints[1].x,
                dstPoints[1].y,
                dstPoints[2].x,
                dstPoints[2].y,
        };
        for (int i = 0; i < 6; ++i) {
            int pivotRow = i;
            for (int j = i + 1; j < 6; ++j) {
                if (Math.abs(A[j][i]) > Math.abs(A[pivotRow][i])) {
                    pivotRow = j;
                }
            }
            if (pivotRow != i) {
                for (int k = 0; k < 6; ++k) {
                    double temp = A[i][k];
                    A[i][k] = A[pivotRow][k];
                    A[pivotRow][k] = temp;
                }
                double tempB = B[i];
                B[i] = B[pivotRow];
                B[pivotRow] = tempB;
            }

            double pivotValue = A[i][i];
            for (int k = i; k < 6; ++k) {
                A[i][k] /= pivotValue;
            }
            B[i] /= pivotValue;

            for (int j = i + 1; j < 6; ++j) {
                double factor = A[j][i];
                for (int k = i; k < 6; ++k) {
                    A[j][k] -= factor * A[i][k];
                }
                B[j] -= factor * B[i];
            }
        }

        for (int i = 4; i >= 0; --i) {
            for (int j = i + 1; j < 6; ++j) {
                B[i] -= A[i][j] * B[j];
            }
        }
        affineTransformation = new double[]{B[0], B[1], B[2], B[3], B[4], B[5]};
    }
}
